#include "number.h"
#include "common.h"
#include <cmath>

const int64_t ZNumber::kAccuracyValue = 1000;

ZNumber::ZNumber() : value_(0) {}

ZNumber::ZNumber(double f) : value_(static_cast<int64_t>(f * kAccuracyValue)) {}

ZNumber::ZNumber(int32_t v) : value_(v * kAccuracyValue) {}

ZNumber::ZNumber(uint32_t v) : value_(v * kAccuracyValue) {}

ZNumber::ZNumber(const ZNumber& f) : value_(f.value_) {}

ZNumber& ZNumber::operator+=(const ZNumber& other) {
  value_ += other.value_;
  return *this;
}

ZNumber& ZNumber::operator-=(const ZNumber& other) {
  value_ -= other.value_;
  return *this;
}

ZNumber& ZNumber::operator*=(const ZNumber& other) {
  value_ *= other.value_;
  value_ /= kAccuracyValue;
  return *this;
}

ZNumber& ZNumber::operator/=(const ZNumber& other) {
  if (other.value_ == 0) {
    return *this;
  }
  value_ = value_ * kAccuracyValue / other.value_;
  return *this;
}

ZNumber& ZNumber::operator%=(const ZNumber& other) {
  if (other.value_ == 0) {
    return *this;
  }
  value_ = value_ % other.value_;
  return *this;
}

ZNumber ZNumber::operator+(double v) {
  ZNumber res(*this);
  return res += v;
}

ZNumber ZNumber::operator-(double v) {
  ZNumber res(*this);
  return res -= v;
}

ZNumber ZNumber::operator*(double v) {
  ZNumber res(*this);
  return res *= v;
}

ZNumber ZNumber::operator/(double v) {
  ZNumber res(*this);
  return res /= v;
}

ZNumber ZNumber::operator%(double v) {
  ZNumber res(*this);
  return res %= v;
}

ZNumber& ZNumber::operator+=(double f) {
  ZNumber other(f);
  return *this += other;
}

ZNumber& ZNumber::operator-=(double f) {
  ZNumber other(f);
  return *this -= other;
}

ZNumber& ZNumber::operator*=(double f) {
  ZNumber other(f);
  return *this *= other;
}

ZNumber& ZNumber::operator/=(double f) {
  ZNumber other(f);
  return *this /= other;
}

ZNumber& ZNumber::operator%=(double f) {
  ZNumber other(f);
  return *this %= other;
}

ZNumber ZNumber::operator+(int32_t v) {
  ZNumber res(*this);
  return res += v;
}

ZNumber ZNumber::operator-(int32_t v) {
  ZNumber res(*this);
  return res -= v;
}

ZNumber ZNumber::operator*(int32_t v) {
  ZNumber res(*this);
  return res *= v;
}

ZNumber ZNumber::operator/(int32_t v) {
  ZNumber res(*this);
  return res /= v;
}

ZNumber ZNumber::operator%(int32_t v) {
  ZNumber res(*this);
  return res %= v;
}

ZNumber& ZNumber::operator+=(int32_t v) {
  ZNumber other(v);
  return *this += other;
}

ZNumber& ZNumber::operator-=(int32_t v) {
  ZNumber other(v);
  return *this -= other;
}

ZNumber& ZNumber::operator*=(int32_t v) {
  ZNumber other(v);
  return *this *= other;
}

ZNumber& ZNumber::operator/=(int32_t v) {
  ZNumber other(v);
  return *this /= other;
}

ZNumber& ZNumber::operator%=(int32_t v) {
  ZNumber other(v);
  return *this %= other;
}

ZNumber ZNumber::operator+(const ZNumber& other) const {
  ZNumber res = *this;
  res += other;
  return res;
}

ZNumber ZNumber::operator-(const ZNumber& other) const {
  ZNumber res = *this;
  res -= other;
  return res;
}

ZNumber ZNumber::operator-() const { return ZNumber::MakeByReal(-value_); }

ZNumber ZNumber::operator*(const ZNumber& other) const {
  ZNumber res = *this;
  res *= other;
  return res;
}

ZNumber ZNumber::operator/(const ZNumber& other) const {
  ZNumber res = *this;
  res /= other;
  return res;
}

ZNumber ZNumber::operator%(const ZNumber& other) const {
  ZNumber res = *this;
  res %= other;
  return res;
}

ZNumber& ZNumber::operator=(const ZNumber& other) {
  value_ = other.value_;
  return *this;
}

bool ZNumber::operator==(const ZNumber& other) const {
  return (value_ == other.value_);
}

bool ZNumber::operator!=(const ZNumber& other) const {
  return (value_ != other.value_);
}

bool ZNumber::operator>(const ZNumber& other) const {
  return (value_ > other.value_);
}

bool ZNumber::operator>=(const ZNumber& other) const {
  return (value_ >= other.value_);
}

bool ZNumber::operator<(const ZNumber& other) const {
  return (value_ < other.value_);
}

bool ZNumber::operator<=(const ZNumber& other) const {
  return (value_ <= other.value_);
}

bool ZNumber::operator==(int32_t v) const {
  return (value_ == v * kAccuracyValue);
}

bool ZNumber::operator!=(int32_t v) const { return !(value_ == v); }

bool ZNumber::operator>(int32_t v) const {
  return (value_ > v * kAccuracyValue);
}

bool ZNumber::operator>=(int32_t v) const {
  return (value_ >= v * kAccuracyValue);
}

bool ZNumber::operator<(int32_t v) const {
  return (value_ < v * kAccuracyValue);
}

bool ZNumber::operator<=(int32_t v) const {
  return (value_ <= v * kAccuracyValue);
}

bool ZNumber::operator==(uint32_t v) const {
  return (value_ == v * kAccuracyValue);
}

bool ZNumber::operator!=(uint32_t v) const { return !(value_ == v); }

bool ZNumber::operator>(uint32_t v) const {
  return (value_ > v * kAccuracyValue);
}

bool ZNumber::operator>=(uint32_t v) const {
  return (value_ >= v * kAccuracyValue);
}

bool ZNumber::operator<(uint32_t v) const {
  return (value_ < v * kAccuracyValue);
}

bool ZNumber::operator<=(uint32_t v) const {
  return (value_ <= v * kAccuracyValue);
}

bool ZNumber::operator==(double v) const {
  return (value_ == v * kAccuracyValue);
}

bool ZNumber::operator!=(double v) const {
  return (value_ != v * kAccuracyValue);
}

bool ZNumber::operator>(double v) const {
  return (value_ > v * kAccuracyValue);
}

bool ZNumber::operator>=(double v) const {
  return (value_ >= v * kAccuracyValue);
}

bool ZNumber::operator<(double v) const {
  return (value_ < v * kAccuracyValue);
}

bool ZNumber::operator<=(double v) const {
  return (value_ <= v * kAccuracyValue);
}

bool ZNumber::IsZero() const { return value_ == 0; }

ZNumber ZNumber::Sqrt() const {
  ZNumber res = *this;
  res.value_ = static_cast<int64_t>(std::sqrt(res.value_ * 1000));
  return res;
}

int64_t ZNumber::GetValue() const { return value_; }

void ZNumber::SetValue(int64_t value) { value_ = value; }

ZNumber FloatSqrt(const ZNumber& value) { return value.Sqrt(); }

ZNumber ZNumber::MakeByReal(int64_t value) {
  ZNumber f;
  f.value_ = value;
  return f;
}

double ZNumber::ToFloat() const {
  double value = static_cast<double>(value_);
  return value / kAccuracyValue;
}

int64_t ZNumber::ToInt() const { return value_ / kAccuracyValue; }

uint32_t ZNumber::ToGrid() const {
  if (value_ < 0) {
    return kInvalidGridIndex;
  }
  return static_cast<uint32_t>(value_ / kAccuracyValue / kGridSize);
}

ZNumber ZNumber::Ceil(ZNumber resolution) const {
  if (resolution.value_ <= 0) {
    return ZNumber();
  }
  ZNumber res;
  res.value_ =
      (value_ + resolution.value_ - 1) / resolution.value_ * resolution.value_;
  return ZNumber(res);
}

ZNumber ZNumber::Floor(ZNumber resolution) const {
  if (resolution.value_ <= 0) {
    return ZNumber();
  }
  ZNumber res;
  res.value_ = value_ / resolution.value_ * resolution.value_;
  return ZNumber(res);
}

ZNumber ZNumber::Abs() const {
  if (value_ > 0) {
    return *this;
  }
  return ZNumber::MakeByReal(-value_);
}

ZNumber ZNumber::Mod(ZNumber mod) const {
  if (mod.value_ == 0) {
    return ZNumber();
  }
  auto new_value = value_ % mod.value_;
  return ZNumber::MakeByReal(new_value);
}
